/*
 * Configuration.java    0.0.1    23/set/2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */ 
package org.mentalsmash.tazio.net.cs.server;

import java.io.File;
import java.io.IOException;

import org.mentalsmash.tazio.utils.ConfigurationFileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class represent a configuration for a {@link CSComFacade CSComFacade} implementation.
 * <p>
 * Use this class to set parameters for the {@link CSComFacade CSComFacade} to be built. Notice that
 * changing values of fields in an instance of {@link Configuration} does not automatically change those
 * values in the related {@link CSComFacade CSComFacade}. See  {@link CSComFacade CSComFacade} documentation
 * to see how to do so.
 * </p>
 * @see CSComFacade
 * @version    0.0.1    23/set/2009    
 * @author     Andrea Reale <andrea.reale@gmail.com>
 */
public final class Configuration {

	private final static Logger log = LoggerFactory.getLogger(Configuration.class);
	/**
	 * The default port number on which the server will listen for client connections
	 */
	public final static int DEFAULT_PORT = 4242;
	/**
	 * The default address the sever will listen on
	 */
	public final static String DEFALUT_ADDRESS = null;
	/**
	 * The default number of warking threads in the thread pool. 
	 * This setting has no effects if the fixedThreadPool property is set to "false"
	 */
	public final static int DEFAULT_MAX_THREADS_ACTIVE = 20;
	/**
	 * The default mode of operation of the working-threads' pool. If this is set
	 * to false, the pool will generate thread as needed, but will reuse existing
	 * threads if they're idle.
	 * Otherwise (i.e. if it is true) it will use a pool of a fixed numer of threads.
	 * <p>T
	 * he number of threads in this fixed side pool is controlled by the
	 * maxThreadsActive property
	 * </p>
	 */
	public final static boolean DEFAULT_FIXED_THREAD_POOL = false; 
	
    /**
     * The cap on the number of "idle" instances in the pool.
     * If maxBuffersIdle is set too low on heavily loaded systems it is possible you
     * will see objects being destroyed and almost immediately new objects
     * being created. This is a result of the active threads momentarily
     * returning objects faster than they are requesting them them, causing the
     * number of idle objects to rise above maxIdle. The best value for maxIdle
     * for heavily loaded system will vary but the default is a good starting
     * point.
     * Use a negative value to indicate an unlimited number of idle instances.
     */
	public final static int  DEFAULT_MAX_BUFFERS_IDLE = 5;
    
	/**
     * The maximum number of objects that can be allocated by the pool
     * (checked out to clients, or idle awaiting checkout) at a given time.
     * When non-positive, there is no limit to the number of objects that can
     * be managed by the pool at one time.
     * <p>
     * This value makes no sense if the pool is allowed to grow
     * </p>
     */
	public final static int DEFAULT_MAX_BUFFERS_ACTIVE = 5;
	
	/**
	 * The default mode of operation of the buffers pool. If this is set
	 * to false, the pool will generate buffers as needed, but will reuse existing
	 * threads if they're idle.
	 * Otherwise (i.e. if it is true) it will use a pool of a fixed number of buffers.
	 * <p>
	 * he number of buffers in this fixed side pool is controlled by the
	 * maxBuffersActive property
	 * </p>
	 */
	public final static boolean DEFAULT_FIXED_BUFFER_POOL = false;
	
	/**
	 * The default size of the buffers returned by the pool. Better if this size
	 * is a power of two
	 */
	public final static int DEFAULT_BUFFER_SIZE = 1024;
	
	/* ********************************************* */
	/* KEYS FOR PROPERTIES IN THE CONFIGURATION FILE */
	/* ********************************************* */
	private final static String PORT_KEY = "port";
	private final static String ADDRESS_KEY = "address";
	private final static String FIXED_TP_KEY = "fixedThreadPool";
	private final static String FIXED_BP_KEY = "fixedBufferPool";
	private final static String MAX_TA_KEY = "maxThreadsActive";
	private final static String MAX_BA_KEY = "maxBuffersActive";
	private final static String MAX_BI_KEY = "maxBuffersIdle";
	private final static String BUFF_SIZE_KEY = "bufferSize";
	
	
	/*
	 * Fields are public, since this class has no logic (except the one of loading itself from a file)
	 * but it's a mere container of values that can be changed without any harm
	 */
	/**
	 * Configures the port on which the server will listen on incoming connections from clients
	 */
	public int portNumber = DEFAULT_PORT;
	
	/**
	 * The string representation of the default address the server will listen on
	 */
	public String address = DEFALUT_ADDRESS;
	/**
	 * The number of warking threads in the thread pool. 
	 * This setting has no effects if the fixedThreadPool property is set to "false"
	 */
	public int maxThreadsActive = DEFAULT_MAX_THREADS_ACTIVE;
	/**
	 * The mode of operation of the working-threads' pool. If this is set
	 * to false, the pool will generate thread as needed, but will reuse existing
	 * threads if they're idle.
	 * Otherwise (i.e. if it is true) it will use a pool of a fixed numer of threads.
	 * <p>T
	 * he number of threads in this fixed side pool is controlled by the
	 * maxThreadsActive property
	 * </p>
	 */
	public boolean fixedThreadPool = DEFAULT_FIXED_THREAD_POOL;
	 /**
     * The cap on the number of "idle" instances in the pool.
     * If maxBuffersIdle is set too low on heavily loaded systems it is possible you
     * will see objects being destroyed and almost immediately new objects
     * being created. This is a result of the active threads momentarily
     * returning objects faster than they are requesting them them, causing the
     * number of idle objects to rise above maxIdle. The best value for maxIdle
     * for heavily loaded system will vary but the default is a good starting
     * point.
     * Use a negative value to indicate an unlimited number of idle instances.
     */
	public int maxBuffersIdle = DEFAULT_MAX_BUFFERS_IDLE;
	/**
     * The maximum number of objects that can be allocated by the pool
     * (checked out to clients, or idle awaiting checkout) at a given time.
     * When non-positive, there is no limit to the number of objects that can
     * be managed by the pool at one time.
     * <p>
     * This value makes no sense if the pool is allowed to grow
     * </p>
     */
	public int maxBuffersActive  = DEFAULT_MAX_BUFFERS_ACTIVE;
	/**
	 * The mode of operation of the buffers pool. If this is set
	 * to false, the pool will generate buffers as needed, but will reuse existing
	 * threads if they're idle.
	 * Otherwise (i.e. if it is true) it will use a pool of a fixed number of buffers.
	 * <p>
	 * he number of buffers in this fixed side pool is controlled by the
	 * maxBuffersActive property
	 * </p>
	 */
	public boolean fixedBufferPool = DEFAULT_FIXED_BUFFER_POOL;
	/**
	 * The size of the buffers returned by the pool. Better if this size
	 * is a power of two
	 */
	public int bufferSize = DEFAULT_BUFFER_SIZE;
	
	/**
	 * Creates a configuration for the <tt>CSComSercice</tt> reading its values from
	 * a file in the file system

	 * @param path the path to reach the file that contains the configuration
	 * @return a {@link Configuration Configuration} instance created with the values
	 *         read in the file in the given <code>path</code>.
	 */
	public static Configuration fromFile(final String path) {
	    ConfigurationFileUtil confUtil = ConfigurationFileUtil.newConfUtil();
	    Configuration conf = new Configuration();

	    try {
		confUtil.load(path);
		conf.bufferSize = confUtil.getIntProperty(BUFF_SIZE_KEY);
		conf.fixedBufferPool = confUtil.getBooleanProperty(FIXED_BP_KEY);
		conf.fixedThreadPool = confUtil.getBooleanProperty(FIXED_TP_KEY);
		conf.maxBuffersActive = confUtil.getIntProperty(MAX_BA_KEY);
		conf.maxBuffersIdle = confUtil.getIntProperty(MAX_BI_KEY);
		conf.maxThreadsActive = confUtil.getIntProperty(MAX_TA_KEY);
		conf.portNumber = confUtil.getIntProperty(PORT_KEY);
		conf.address = confUtil.getStringProperty(ADDRESS_KEY);
	    } catch (IOException e) {
		log.warn("Can't load from file. Default conf will be used instead ", e);
		conf = Configuration.defaultConfiguration();
	    }


	    return conf;

	}
	
	/**
	 * Creates a configuration for the <tt>CSComSercice</tt> reading its values from
	 * a file in the file system
	 * @param file an open and readable file instance pointing to the file containing values
	 *             for this configuration
	 * @return a {@link Configuration Configuration} instance created with the values
	 *         read in the file in the given <code>path</code>.
	 */
	public static Configuration fromFile(final File file) {
	    try {
		return fromFile(file.getCanonicalPath());
	    } catch (IOException e) {
		log.warn("Can't load from file. Default conf will be used instead ", e);
		return Configuration.defaultConfiguration();
	    }
	}

	/**
	 * Returns a configuration object having the default parameters set.
	 * <p>
	 * Currently that's the same that calling the default constructor, but this may change
	 * in future
	 * </p>
	 * @return An instance of <tt>Configuration</tt> with default values
	 */
	public static Configuration defaultConfiguration() {
		return new Configuration();
	}
}
